package com.wxapp.admin.service.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.wxapp.admin.api.pojo.entity.UserDO;
import com.wxapp.admin.api.pojo.vm.login.PUserEventVm;
import com.wxapp.admin.api.pojo.vm.login.WechatOpenIdVm;
import com.wxapp.admin.api.pojo.vo.login.QLoginVo;
import com.wxapp.admin.service.event.UserEvent;
import com.wxapp.admin.service.mapper.UserMapper;
import com.wxapp.admin.service.service.LoginService;
import com.wxapp.common.base.util.jackson.JacksonUtil;
import com.wxapp.common.base.util.redis.RedisUtil;
import com.wxapp.common.constant.base.IBaseService;
import com.wxapp.common.constant.constant.LoginConstant;
import com.wxapp.common.constant.enums.ClientTypeEnum;
import com.wxapp.common.constant.pojo.vm.LoginInfoVm;
import com.wxapp.common.constant.pojo.vm.PermissionVm;
import com.wxapp.common.constant.pojo.vm.ResultVm;
import com.wxapp.common.constant.pojo.vo.HttpRequestVo;
import com.wxapp.common.constant.pojo.vo.UserVo;
import com.wxapp.common.constant.properties.LoginProperties;
import com.wxapp.common.constant.properties.WechatProperties;
import com.wxapp.common.core.annotation.util.IpUtil;
import com.wxapp.common.core.filter.except.ThrowMyException;
import com.wxapp.common.util.check.CheckUtil;
import com.wxapp.common.util.copy.CopyUtil;
import com.wxapp.common.util.http.HttpRequestUtil;
import com.wxapp.common.util.jwt.JWTUtil;
import com.wxapp.common.util.random.RandomUtil;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;

@Service
@RequiredArgsConstructor
public class LoginServiceImpl extends IBaseService implements LoginService {
    private final UserMapper userMapper;
    private final RedisUtil redisUtil;
    private final JWTUtil jwtUtil;
    private final LoginProperties loginProperties;
    private final WechatProperties wechatProperties;
    private final ApplicationEventPublisher publisher;

    @Override
    public ResultVm<String> login(QLoginVo qLoginVo, HttpServletRequest request) throws ThrowMyException {
        if (qLoginVo.getType() == ClientTypeEnum.WECHAT) {
            qLoginVo.setLoginName(getOpenId(qLoginVo.getLoginName()));
        }
        var query = new LambdaQueryWrapper<UserDO>();
        query.and(s -> s.eq(UserDO::getLoginName, qLoginVo.getLoginName())
                        .or(st -> st.eq(UserDO::getCode, qLoginVo.getLoginName())))
                .eq(UserDO::getType,qLoginVo.getType())
                .eq(UserDO::getStop, false)
                .eq(UserDO::getWork, true)
                .eq(UserDO::getStatus, true);
        UserDO user = userMapper.selectFirst(query);
        if (checkIsLogin(user, qLoginVo)) {
            throw new ThrowMyException("用户名密码错误");
        }
        return setPermission(qLoginVo, user,  request);
    }
    /**
     * 推出登录
     */
    @Override
    public ResultVm<String> logOff(UserVo userVo) {
        redisUtil.del(LoginConstant.PERMISSION_LIST + userVo.getId());
        return success();
    }

    /**
     * 检查是否通过登录
     */
    private boolean checkIsLogin(UserDO sysUserDO, QLoginVo qLoginVo) {
        boolean result = false;
        switch (qLoginVo.getType()) {
            case WEB:
                result = sysUserDO == null
                        || CheckUtil.isNullObject(sysUserDO)
                        || !sysUserDO.getPassword().equals(RandomUtil.md5BySalt(qLoginVo.getPassword(), sysUserDO.getSalt()));
                break;
            case INSIDER:
                result = sysUserDO == null || CheckUtil.isNullObject(sysUserDO);
                break;
            default:
                break;
        }
        return result;
    }

    /**
     * 把用户信息权限和角色放进缓存
     */
    private ResultVm<String> setPermission(QLoginVo qLoginVo, UserDO user, HttpServletRequest request) {
        var infoVo = new LoginInfoVm();
        switch (qLoginVo.getType()) {
            case WEB:
                List<PermissionVm> permissionDos = userMapper.getGrantedAuthorities(user.getId());
                if (CheckUtil.isNullList(permissionDos)) {
                    break;
                }
                infoVo.setPermissions(permissionDos);
                break;
            case WECHAT:
                infoVo.setPermissions(List.of(new PermissionVm(null, "wechat")));
                if (CheckUtil.isNullObject(user)) {
                    user = saveUser(qLoginVo.getLoginName());
                }
                break;
            case INSIDER:
                infoVo.setPermissions(List.of(new PermissionVm(null, "insider")));
                break;
            default:
                break;
        }
        UserVo userVo = CopyUtil.copyObjectNew(user, UserVo::new, (s, r) -> {
            r.setUserType(qLoginVo.getType());
            return null;
        });
        String userInfo=JacksonUtil.toJSONString(userVo);
        var token = jwtUtil.createToken(request,userInfo);
        infoVo.setToken(token);
        redisUtil.set(LoginConstant.PERMISSION_LIST + user.getId(), infoVo, loginProperties.getExpireTime());
        publisher.publishEvent(new UserEvent(this, "用户登录", new PUserEventVm(user, IpUtil.getIp(request))));
        return success("登录成功", token);
    }

    private UserDO saveUser(String wxCode) {
        UserDO user = new UserDO();
        user.setCode(wxCode);
        user.setLoginName(RandomUtil.getCommonRandom(6));
        user.setCreateTime(LocalDateTime.now());
        user.setType(ClientTypeEnum.WECHAT);
        user.setWork(true);
        user.setStop(false);
        userMapper.insert(user);
        return user;
    }

    /**
     * 获取微信信息
     */
    private String getOpenId(String wxCode) throws ThrowMyException {
        String openIdUrl = "https://api.weixin.qq.com/sns/jscode2session";
        var maps = Map.of("appid", wechatProperties.getAppid(), "secret", wechatProperties.getSecret(), "js_code", wxCode, "grant_type", "authorization_code");
        var requestVo = new HttpRequestVo<>(openIdUrl, HttpMethod.GET, maps, WechatOpenIdVm.class);
        var request = HttpRequestUtil.request(requestVo);
        if (CheckUtil.isNullObject(request) || request.getErrcode() != null) {
            throw new ThrowMyException("获取用户信息失败");
        }
        return request.getOpenid();
    }
}
